/* Includes ------------------------------------------------------------------*/
#include "SystemTick.h"
#include "Led.h"
#include "Mouse.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define CLK_PORT     (GPIOC)
#define CLK_PINS     (GPIO_PIN_5)

#define DATA_PORT    (GPIOC)
#define DATA_PINS    (GPIO_PIN_6)

#define CLK_IN       (CLK_PORT->IDR & CLK_PINS)
#define DATA_IN      (DATA_PORT->IDR & DATA_PINS)

#define SET_CLK      {(CLK_PORT->ODR |= (uint8_t)CLK_PINS); DelayUs(3);}
#define RESET_CLK    {(CLK_PORT->ODR &= (uint8_t)(~CLK_PINS)); DelayUs(3);}

#define SET_DATA     DATA_PORT->ODR |= (uint8_t)DATA_PINS
#define RESET_DATA   DATA_PORT->ODR &= (uint8_t)(~DATA_PINS)


/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#define MOUSE_PRESCALE      1000/100        // 1 giay lay mau 100 lan
bool s_Mouse_IntFlag = FALSE;
u16  s_Mouse_PrescaleCnt = 0;

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
#define TIMEOUT_WAIT_HIGH   100000
#define TIMEOUT_WAIT_LOW    10000
bool s_InterfaceErrorFlag = FALSE;
s8   s_MouseStatus;
s16  s_TotalDeltaX = 0;
s16  s_TotalDeltaY = 0;

/* Public functions ----------------------------------------------------------*/


void Mouse_TimerInterrupt(void)
{
    if (MOUSE_PRESCALE <= ++s_Mouse_PrescaleCnt)
    {
        s_Mouse_PrescaleCnt = 0;
        s_Mouse_IntFlag = TRUE;
    }
}


void DelayUs(u16 iVal)
{
    while(iVal--)
    {
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
        nop();
    }
}

void DelayMs(u16 iVal)
{
    while(iVal--)
    {
        DelayUs(1000);
    }
}

#if 1
#define Mouse_WhileClkHigh()  while (CLK_IN > 0)
#define Mouse_WhileClkLow()   while (CLK_IN == 0)
#else
void Mouse_WhileClkHigh(void)
{
    u32 timeoutCnt = TIMEOUT_WAIT_HIGH;
    while (timeoutCnt--)
    {
        if (CLK_IN == 0)
        {
            return;
        }
    }
    
    s_InterfaceErrorFlag = TRUE;
}


void Mouse_WhileClkLow(void)
{
    u16 timeoutCnt = TIMEOUT_WAIT_LOW;
    while (timeoutCnt--)
    {
        if (CLK_IN > 0)
        {
            return;
        }
    }

    s_InterfaceErrorFlag = TRUE;
}
#endif

void Mouse_Write(u8 data)
{
    u8 i;
    u8 parity = 1;

    /* put pins in output mode */
    SET_DATA;
    SET_CLK;
    DelayUs(300);
    RESET_CLK;
    DelayUs(300);
    RESET_DATA;
    DelayUs(10);

    /* start bit */
    SET_CLK;
    /* wait for mouse to take control of clock); */
    DelayUs(1);
    Mouse_WhileClkHigh();
    /* clock is low, and we are clear to send data */
    for (i=0; i < 8; i++) {
        if (data & 0x01) {
            SET_DATA;
        } 
        else {
            RESET_DATA;
        }
        /* wait for clock cycle */
        DelayUs(5);
        Mouse_WhileClkLow();
        DelayUs(10);
        Mouse_WhileClkHigh();
        parity = parity ^ (data & 0x01);
        data = data >> 1;
    }  
    /* parity */
    if (parity) {
        SET_DATA;
    } 
    else {
        RESET_DATA;
    }
    Mouse_WhileClkLow();
    DelayUs(10);
    Mouse_WhileClkHigh();
    /* stop bit */
    SET_DATA;
    DelayUs(50);
    Mouse_WhileClkHigh();
    DelayUs(10);
    /* wait for mouse to switch modes */
    while ((0 == CLK_IN) || (0 == DATA_IN));
    /* put a hold on the incoming data. */
    //RESET_CLK;
}

/*
* Get a byte of data from the mouse
*/
s8 Mouse_Read(void)
{
    s8 data = 0x00;
    u8 i;
    s8 mask = 0x01;

    /* start the clock */
    SET_CLK;
    SET_DATA;
    DelayUs(10);

    //while (SET == GPIO_ReadInputPin(CLK_PORT, CLK_PINS));
    Mouse_WhileClkHigh();
    DelayUs(5);  /* not sure why */
    //while(RESET == GPIO_ReadInputPin(CLK_PORT, CLK_PINS));
    Mouse_WhileClkLow();
    for (i=0; i < 8; i++) {
        //while(SET == GPIO_ReadInputPin(CLK_PORT, CLK_PINS));
        DelayUs(5);
        Mouse_WhileClkHigh();
        if (0 < DATA_IN) {
            data = data | mask;
        }
        //while (RESET == GPIO_ReadInputPin(CLK_PORT, CLK_PINS));
        Mouse_WhileClkLow();
        mask = mask << 1;
    }
    /* eat parity bit, which we ignore */
    //while (SET == GPIO_ReadInputPin(CLK_PORT, CLK_PINS));
    DelayUs(5);
    Mouse_WhileClkHigh();
    //while (RESET == GPIO_ReadInputPin(CLK_PORT, CLK_PINS));
    DelayUs(5);
    Mouse_WhileClkLow();
    /* eat stop bit */
    //while (SET == GPIO_ReadInputPin(CLK_PORT, CLK_PINS));
    DelayUs(5);
    Mouse_WhileClkHigh();
    //while (RESET == GPIO_ReadInputPin(CLK_PORT, CLK_PINS));
    DelayUs(5);
    Mouse_WhileClkLow();

    /* put a hold on the incoming data. */
    SET_CLK;
    return (data);
}


bool Mouse_Reset(void)
{
    s8 cr1, cr2, cr3;

    GPIO_Init(CLK_PORT, CLK_PINS, GPIO_MODE_OUT_OD_HIZ_FAST);
    SET_CLK;

    GPIO_Init(DATA_PORT, DATA_PINS, GPIO_MODE_OUT_OD_HIZ_FAST);
    SET_DATA;

    Mouse_Write(0xff);  // reset mouse
    cr1 = Mouse_Read(); // ack
    Mouse_Read();       // blank
    Mouse_Read();       // blank

    Mouse_Write(0xf0);  // remote mode
    cr2 = Mouse_Read(); // ack

    Mouse_Write(0xf4);  // enable device
    cr3 = Mouse_Read(); // ack

    DelayUs(100);
    if ( ( (u8)cr1 & (u8)cr2 & (u8)cr3 ) == 0xF4 ) return (TRUE);
    else return (FALSE);
}


void Mouse_Init(void)
{
    Mouse_Reset();
    SysTick_Registration(&Mouse_TimerInterrupt);
}


void Mouse_GetPara(void)
{
    s8  deltaX = 0;
    s8  deltaY = 0;

    // get a reading from the mouse 
    Mouse_Write(0xeb);              // give me data!
    Mouse_Read();
    s_MouseStatus = Mouse_Read();   // get button status
    deltaX        = Mouse_Read();   // get deltaX
    deltaY        = Mouse_Read();   // get deltaY
    s_TotalDeltaX += deltaX;
    s_TotalDeltaY += deltaY;
    //DelayUs(20);                    // twiddle
}


u8 Mouse_GetStatus(void)
{
    u8 rc = s_MouseStatus;
    s_MouseStatus = 0x08;
    return (rc);
}


u16 Mouse_GetTotalDeltaX(void)
{
    u16 rc = s_TotalDeltaX;
    s_TotalDeltaX = 0;
    return (rc);
}


u16 Mouse_GetTotalDeltaY(void)
{
    u16 rc = s_TotalDeltaY;
    s_TotalDeltaY = 0;
    return (rc);
}


void Mouse_RunOne(void)
{
    if (TRUE == s_Mouse_IntFlag)
    {
        s_Mouse_IntFlag = FALSE;
        Mouse_GetPara();
    }
}
